/*
* 梁山派软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网：www.lckfb.com
* 技术支持常驻论坛，任何技术问题欢迎随时交流学习
* 立创论坛：club.szlcsc.com
* 关注bilibili账号：【立创开发板】，掌握我们的最新动态！
* 不靠卖板赚钱，以培养中国工程师为己任
* Change Logs:
* Date           Author       Notes
* 2024-06-12     DAWN    first version
*/

#include "ILI9341.h"

/** 
  * @brief ILI9341 chip IDs  
  */ 
#define ILI9341_ID                  0x9341

/** 
  * @brief  ILI9341 Size  
  */  
#define  ILI9341_LCD_PIXEL_WIDTH    ((uint16_t)240)
#define  ILI9341_LCD_PIXEL_HEIGHT   ((uint16_t)320)

/** 
  * @brief  ILI9341 Timing  
  */     
/* Timing configuration  (Typical configuration from ILI9341 datasheet)
  HSYNC=10 (9+1)
  HBP=20 (29-10+1)
  ActiveW=240 (269-20-10+1)
  HFP=10 (279-240-20-10+1)

  VSYNC=2 (1+1)
  VBP=2 (3-2+1)
  ActiveH=320 (323-2-2+1)
  VFP=4 (327-320-2-2+1)
*/
#define  ILI9341_HSYNC            ((uint32_t)9)   /* Horizontal synchronization */
#define  ILI9341_HBP              ((uint32_t)29)    /* Horizontal back porch      */ 
#define  ILI9341_HFP              ((uint32_t)2)    /* Horizontal front porch     */
#define  ILI9341_VSYNC            ((uint32_t)1)   /* Vertical synchronization   */
#define  ILI9341_VBP              ((uint32_t)3)    /* Vertical back porch        */
#define  ILI9341_VFP              ((uint32_t)2)    /* Vertical front porch       */

/** 
  * @brief  ILI9341 Registers  
  */

/* Level 1 Commands */
#define LCD_SWRESET             0x01   /* Software Reset */
#define LCD_READ_DISPLAY_ID     0x04   /* Read display identification information */
#define LCD_RDDST               0x09   /* Read Display Status */
#define LCD_RDDPM               0x0A   /* Read Display Power Mode */
#define LCD_RDDMADCTL           0x0B   /* Read Display MADCTL */
#define LCD_RDDCOLMOD           0x0C   /* Read Display Pixel Format */
#define LCD_RDDIM               0x0D   /* Read Display Image Format */
#define LCD_RDDSM               0x0E   /* Read Display Signal Mode */
#define LCD_RDDSDR              0x0F   /* Read Display Self-Diagnostic Result */
#define LCD_SPLIN               0x10   /* Enter Sleep Mode */
#define LCD_SLEEP_OUT           0x11   /* Sleep out register */
#define LCD_PTLON               0x12   /* Partial Mode ON */
#define LCD_NORMAL_MODE_ON      0x13   /* Normal Display Mode ON */
#define LCD_DINVOFF             0x20   /* Display Inversion OFF */
#define LCD_DINVON              0x21   /* Display Inversion ON */
#define LCD_GAMMA               0x26   /* Gamma register */
#define LCD_DISPLAY_OFF         0x28   /* Display off register */
#define LCD_DISPLAY_ON          0x29   /* Display on register */
#define LCD_COLUMN_ADDR         0x2A   /* Colomn address register */ 
#define LCD_PAGE_ADDR           0x2B   /* Page address register */ 
#define LCD_GRAM                0x2C   /* GRAM register */   
#define LCD_RGBSET              0x2D   /* Color SET */   
#define LCD_RAMRD               0x2E   /* Memory Read */   
#define LCD_PLTAR               0x30   /* Partial Area */   
#define LCD_VSCRDEF             0x33   /* Vertical Scrolling Definition */   
#define LCD_TEOFF               0x34   /* Tearing Effect Line OFF */   
#define LCD_TEON                0x35   /* Tearing Effect Line ON */   
#define LCD_MAC                 0x36   /* Memory Access Control register*/
#define LCD_VSCRSADD            0x37   /* Vertical Scrolling Start Address */   
#define LCD_IDMOFF              0x38   /* Idle Mode OFF */   
#define LCD_IDMON               0x39   /* Idle Mode ON */   
#define LCD_PIXEL_FORMAT        0x3A   /* Pixel Format register */
#define LCD_WRITE_MEM_CONTINUE  0x3C   /* Write Memory Continue */   
#define LCD_READ_MEM_CONTINUE   0x3E   /* Read Memory Continue */   
#define LCD_SET_TEAR_SCANLINE   0x44   /* Set Tear Scanline */   
#define LCD_GET_SCANLINE        0x45   /* Get Scanline */   
#define LCD_WDB                 0x51   /* Write Brightness Display register */
#define LCD_RDDISBV             0x52   /* Read Display Brightness */   
#define LCD_WCD                 0x53   /* Write Control Display register*/
#define LCD_RDCTRLD             0x54   /* Read CTRL Display */   
#define LCD_WRCABC              0x55   /* Write Content Adaptive Brightness Control */   
#define LCD_RDCABC              0x56   /* Read Content Adaptive Brightness Control */   
#define LCD_WRITE_CABC          0x5E   /* Write CABC Minimum Brightness */   
#define LCD_READ_CABC           0x5F   /* Read CABC Minimum Brightness */   
#define LCD_READ_ID1            0xDA   /* Read ID1 */
#define LCD_READ_ID2            0xDB   /* Read ID2 */
#define LCD_READ_ID3            0xDC   /* Read ID3 */

/* Level 2 Commands */
#define LCD_RGB_INTERFACE       0xB0   /* RGB Interface Signal Control */
#define LCD_FRMCTR1             0xB1   /* Frame Rate Control (In Normal Mode) */
#define LCD_FRMCTR2             0xB2   /* Frame Rate Control (In Idle Mode) */
#define LCD_FRMCTR3             0xB3   /* Frame Rate Control (In Partial Mode) */
#define LCD_INVTR               0xB4   /* Display Inversion Control */
#define LCD_BPC                 0xB5   /* Blanking Porch Control register */
#define LCD_DFC                 0xB6   /* Display Function Control register */
#define LCD_ETMOD               0xB7   /* Entry Mode Set */
#define LCD_BACKLIGHT1          0xB8   /* Backlight Control 1 */
#define LCD_BACKLIGHT2          0xB9   /* Backlight Control 2 */
#define LCD_BACKLIGHT3          0xBA   /* Backlight Control 3 */
#define LCD_BACKLIGHT4          0xBB   /* Backlight Control 4 */
#define LCD_BACKLIGHT5          0xBC   /* Backlight Control 5 */
#define LCD_BACKLIGHT7          0xBE   /* Backlight Control 7 */
#define LCD_BACKLIGHT8          0xBF   /* Backlight Control 8 */
#define LCD_POWER1              0xC0   /* Power Control 1 register */
#define LCD_POWER2              0xC1   /* Power Control 2 register */
#define LCD_VCOM1               0xC5   /* VCOM Control 1 register */
#define LCD_VCOM2               0xC7   /* VCOM Control 2 register */
#define LCD_NVMWR               0xD0   /* NV Memory Write */
#define LCD_NVMPKEY             0xD1   /* NV Memory Protection Key */
#define LCD_RDNVM               0xD2   /* NV Memory Status Read */
#define LCD_READ_ID4            0xD3   /* Read ID4 */
#define LCD_PGAMMA              0xE0   /* Positive Gamma Correction register */
#define LCD_NGAMMA              0xE1   /* Negative Gamma Correction register */
#define LCD_DGAMCTRL1           0xE2   /* Digital Gamma Control 1 */
#define LCD_DGAMCTRL2           0xE3   /* Digital Gamma Control 2 */
#define LCD_INTERFACE           0xF6   /* Interface control register */

/* Extend register commands */
#define LCD_POWERA               0xCB   /* Power control A register */
#define LCD_POWERB               0xCF   /* Power control B register */
#define LCD_DTCA                 0xE8   /* Driver timing control A */
#define LCD_DTCB                 0xEA   /* Driver timing control B */
#define LCD_POWER_SEQ            0xED   /* Power on sequence register */
#define LCD_3GAMMA_EN            0xF2   /* 3 Gamma enable register */
#define LCD_PRC                  0xF7   /* Pump ratio control register */

/* Size of read registers */
#define LCD_READ_ID4_SIZE        3      /* Size of Read ID4 */


//使用位带操作
#define USE_BIT

#ifdef USE_BIT

#define BIT_ADDR(byte_offset,bitnum)  (volatile unsigned long*)(0x42000000 + (byte_offset * 32) + (bitnum * 4))

// PE输出对应的字节偏移量为
#define GPIOE_OCTL_OFFSET    ((GPIOE + 0x14) - 0x40000000)

// PE输入对应的字节偏移量为
#define GPIOE_ISTAT_OFFSET   ((GPIOE + 0x10) - 0x40000000)

// PC输出对应的字节偏移量为
#define GPIOC_OCTL_OFFSET    ((GPIOC + 0x14) - 0x40000000)

// PC输入对应的字节偏移量为
#define GPIOC_ISTAT_OFFSET   ((GPIOC + 0x10) - 0x40000000)

// PE输出最终可配置为
#define PEout(n)    *(BIT_ADDR(GPIOE_OCTL_OFFSET,n))         // 输出

// PE输入最终可配置为
#define PEin(n)        *(BIT_ADDR(GPIOE_ISTAT_OFFSET,n))     // 输入

// PC输出最终可配置为
#define PCout(n)    *(BIT_ADDR(GPIOC_OCTL_OFFSET,n))         // 输出

// PC输入最终可配置为
#define PCin(n)        *(BIT_ADDR(GPIOC_ISTAT_OFFSET,n))     // 输入

// 这里的n就是对应的第几位（n取值范围为0-15）。

#define LCD_CS_H PCout(0) = 1;
#define LCD_CS_L PCout(0) = 0;
#define LCD_RS_H PCout(1) = 1;
#define LCD_RS_L PCout(1) = 0;
#define LCD_WR_H PCout(2) = 1;
#define LCD_WR_L PCout(2) = 0;
#define LCD_RD_H PCout(3) = 1;
#define LCD_RD_L PCout(3) = 0;
#define LCD_REST_H PCout(4) = 1;
#define LCD_REST_L PCout(4) = 0;
#else
#define LCD_CS_H gpio_bit_set(LCD_CONTORL_PORT,LCD_CS);
#define LCD_CS_L gpio_bit_reset(LCD_CONTORL_PORT,LCD_CS);
#define LCD_RS_H gpio_bit_set(LCD_CONTORL_PORT,LCD_RS);
#define LCD_RS_L gpio_bit_reset(LCD_CONTORL_PORT,LCD_RS);
#define LCD_WR_H gpio_bit_set(LCD_CONTORL_PORT,LCD_WR);
#define LCD_WR_L gpio_bit_reset(LCD_CONTORL_PORT,LCD_WR);
#define LCD_RD_H gpio_bit_set(LCD_CONTORL_PORT,LCD_RD);
#define LCD_RD_L gpio_bit_reset(LCD_CONTORL_PORT,LCD_RD);
#define LCD_REST_H gpio_bit_set(LCD_CONTORL_PORT,LCD_REST);
#define LCD_REST_L gpio_bit_reset(LCD_CONTORL_PORT,LCD_REST);
#endif

/**
-  @brief  引脚初始化
 -  @note   /CS -> PC0
            RS -> PC1
            /WR -> PC2
            /RD -> PC3
            /REST -> PC4
            DATA ->PE0-PE15
 -  @param  None
 -  @retval None
*/
void  LCD_IO_Init(void)
{
    //开启时钟
    rcu_periph_clock_enable(RCU_GPIOC);
    rcu_periph_clock_enable(RCU_GPIOE);
    //设置工作模式
    gpio_mode_set(LCD_CONTORL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, LCD_CS|LCD_RS|LCD_WR|LCD_RD|LCD_REST);
    gpio_mode_set(LCD_DATA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_ALL);
    //输出模式设置
    gpio_output_options_set(LCD_CONTORL_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, LCD_CS|LCD_RS|LCD_WR|LCD_RD|LCD_REST);//推挽输出，速度为2MHZ
    gpio_output_options_set(LCD_DATA_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_ALL);//推挽输出，速度为50MHZ

    LCD_REST_H
    LCD_Delay(300);
    LCD_REST_L
    LCD_Delay(300);
    LCD_REST_H
}

/**
-  @brief  写数据（数据）
 -  @note   DC为高电平
 -  @param  RegValue:寄存器的值
 -  @retval None
*/
void  LCD_IO_WriteData(uint16_t RegValue)
{
#ifdef USE_BIT
    LCD_CS_L
    LCD_RS_H
    LCD_WR_L
    for(int i = 0; i < 16; i++)
    {
        PEout(i) = (RegValue>>i) & 0x01;
    }
    LCD_WR_H
    LCD_CS_H
#else

#endif
}

/**
 -  @brief  写地址（命令）
 -  @note   DC为低电平
 -  @param  RegValue:寄存器的地址
 -  @retval None
*/
void  LCD_IO_WriteReg(uint8_t Reg)
{
#ifdef USE_BIT
    LCD_CS_L
    LCD_RS_L
    LCD_WR_L
    for(int i = 0; i < 8; i++)
    {
        PEout(i) = (Reg>>i) & 0x01;
    }
    for(int i = 8; i < 16; i++)
    {
        PEout(i) = 0;
    }
    LCD_WR_H
    LCD_CS_H
#else

#endif
}

/**
 -  @brief  读取数据
 -  @note   DC为低电平
 -  @param  RegValue:寄存器的地址
            ReadSize：读取字节数
 -  @retval 读到的数据
*/
uint16_t LCD_IO_ReadData(void)
{
    volatile uint16_t data=0x0000;
    
    //设置工作模式
    gpio_mode_set(LCD_DATA_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_ALL);
#ifdef USE_BIT
    LCD_CS_L
    LCD_RS_H
    LCD_RD_L
    LCD_Delay(200);
    for(int i = 0; i < 16; i++)
    {
        data = data | PEin(i) << i;
    }
    LCD_RD_H
    LCD_CS_H

#else
    //data = gpio_input_port_get(GPIOE);
#endif

    
    return data;
}

/**
 -  @brief  延时
 -  @note   None
 -  @param  delay:延时的毫秒
 -  @retval None
*/
void LCD_Delay (uint32_t delay)
{
    delay_ms(delay);
}

/**
  * @brief  初始化LCD
  * @param  None
  * @retval None
  */
void ili9341_Init(void)
{
  /* 引脚初始化 */
  LCD_IO_Init();
  
  /* 配置 LCD 寄存器 */

  ili9341_WriteReg(LCD_POWERB);
  ili9341_WriteData(0x00);
  ili9341_WriteData(0xC1);
  ili9341_WriteData(0x30);
  ili9341_WriteReg(LCD_POWER_SEQ);
  ili9341_WriteData(0x64);
  ili9341_WriteData(0x03);
  ili9341_WriteData(0x12);
  ili9341_WriteData(0x81);
  ili9341_WriteReg(LCD_DTCA);
  ili9341_WriteData(0x85);
  ili9341_WriteData(0x00);
  ili9341_WriteData(0x78);
  ili9341_WriteReg(LCD_POWERA);
  ili9341_WriteData(0x39);
  ili9341_WriteData(0x2C);
  ili9341_WriteData(0x00);
  ili9341_WriteData(0x34);
  ili9341_WriteData(0x02);
  ili9341_WriteReg(LCD_PRC);
  ili9341_WriteData(0x20);
  ili9341_WriteReg(LCD_DTCB);
  ili9341_WriteData(0x00);
  ili9341_WriteData(0x00);
  
  ili9341_WriteReg(LCD_FRMCTR1);
  ili9341_WriteData(0x00);
  ili9341_WriteData(0x18);
  ili9341_WriteReg(LCD_DFC);
  ili9341_WriteData(0x0A);
  ili9341_WriteData(0xA2);
  ili9341_WriteReg(LCD_POWER1);
  ili9341_WriteData(0x23);
  ili9341_WriteReg(LCD_POWER2);
  ili9341_WriteData(0x12);
  ili9341_WriteReg(LCD_VCOM1);
  ili9341_WriteData(0x40);
  ili9341_WriteData(0x30);
  ili9341_WriteReg(LCD_VCOM2);
  ili9341_WriteData(0xA8);
  ili9341_WriteReg(LCD_MAC);
  ili9341_WriteData(0x08);
  ili9341_WriteReg(LCD_3GAMMA_EN);
  ili9341_WriteData(0x00);


  /* Colomn address set */
  ili9341_WriteReg(LCD_COLUMN_ADDR);
  ili9341_WriteData(0x00);
  ili9341_WriteData(0x00);
  ili9341_WriteData(0x00);
  ili9341_WriteData(0xEF);
  
  /* Page address set */
  ili9341_WriteReg(LCD_PAGE_ADDR);
  ili9341_WriteData(0x00);
  ili9341_WriteData(0x00);
  ili9341_WriteData(0x01);
  ili9341_WriteData(0x3F);
  ili9341_WriteReg(LCD_INTERFACE);
  ili9341_WriteData(0x01);
  ili9341_WriteData(0x00);
  ili9341_WriteData(0x06);
  
  ili9341_WriteReg(LCD_GRAM);
  LCD_Delay(200);
  
  ili9341_WriteReg(LCD_GAMMA);
  ili9341_WriteData(0x01);
  
  ili9341_WriteReg(LCD_PGAMMA);
  ili9341_WriteData(0x0F);
  ili9341_WriteData(0x29);
  ili9341_WriteData(0x24);
  ili9341_WriteData(0x0C);
  ili9341_WriteData(0x0E);
  ili9341_WriteData(0x09);
  ili9341_WriteData(0x4E);
  ili9341_WriteData(0x78);
  ili9341_WriteData(0x3C);
  ili9341_WriteData(0x09);
  ili9341_WriteData(0x13);
  ili9341_WriteData(0x05);
  ili9341_WriteData(0x17);
  ili9341_WriteData(0x11);
  ili9341_WriteData(0x00);
  ili9341_WriteReg(LCD_NGAMMA);
  ili9341_WriteData(0x00);
  ili9341_WriteData(0x16);
  ili9341_WriteData(0x1B);
  ili9341_WriteData(0x04);
  ili9341_WriteData(0x11);
  ili9341_WriteData(0x07);
  ili9341_WriteData(0x31);
  ili9341_WriteData(0x33);
  ili9341_WriteData(0x42);
  ili9341_WriteData(0x05);
  ili9341_WriteData(0x0C);
  ili9341_WriteData(0x0A);
  ili9341_WriteData(0x28);
  ili9341_WriteData(0x2F);
  ili9341_WriteData(0x0F);
  
  ili9341_WriteReg(LCD_SLEEP_OUT);
  LCD_Delay(200);
  ili9341_WriteReg(LCD_DISPLAY_ON);
  /* GRAM start writing */
  ili9341_WriteReg(LCD_GRAM);
}

/**
  * @brief  读取ID
  * @param  None
  * @retval ID信息
  */
uint16_t ili9341_ReadID(void)
{
    uint16_t id=0;
    
    //发送读取指令
    LCD_IO_WriteReg(LCD_READ_ID4);
    
    LCD_IO_ReadData();//无用信息
    LCD_IO_ReadData();
    
    id = LCD_IO_ReadData();
    id = id << 8;
    id |= LCD_IO_ReadData();
    
    //设置工作模式
    gpio_mode_set(LCD_DATA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_ALL);
  return id;
}

/**
  * @brief  打开屏幕
  * @param  None
  * @retval None
  */
void ili9341_DisplayOn(void)
{
    /* 显示开 */
  ili9341_WriteReg(LCD_DISPLAY_ON);
}

/**
  * @brief  关闭屏幕
  * @param  None
  * @retval None
  */
void ili9341_DisplayOff(void)
{
    /* 显示关 */
  ili9341_WriteReg(LCD_DISPLAY_OFF);
}

/**
  * @brief  写指令
  * @param  LCD_Reg: 寄存器地址
  * @retval None
  */
void ili9341_WriteReg(uint8_t LCD_Reg)
{
  LCD_IO_WriteReg(LCD_Reg);
}

/**
  * @brief  写数据
  * @param  RegValue: 数据
  * @retval None
  */
void ili9341_WriteData(uint16_t RegValue)
{
  LCD_IO_WriteData(RegValue);
}

